#include <iostream>
#include <algorithm>
#include <string>
#include <memory>
#include <boost/lexical_cast.hpp>
#include <unordered_map>
#include <yaml-cpp/yaml.h>
#include <vector>
#include <unordered_set>
#include <functional>
#include <map>
#include <sstream>

class ConfigVarBase {
public:
    typedef std::shared_ptr<ConfigVarBase> ptr;
    ConfigVarBase(const std::string& name, const std::string& description = "")
        :m_name(name)
        ,m_description(description) {
        //将名称变成大小写不明感的
        std::transform(m_name.begin(), m_name.end(), m_name.begin(), ::tolower);
    }
    //有具体类型的子类，所以需要变成 虚析构
    virtual ~ConfigVarBase() {}

    const std::string& getName() const { return m_name;}
    const std::string& getDescription() const { return m_description;}

    virtual std::string toString() = 0;
    virtual bool fromString(const std::string& val) = 0;
    virtual std::string getTypeName() const = 0;
protected:
    std::string m_name;
    std::string m_description;
};

//处理基本类型转换
template<class F, class T>  // 把F类型转换为T类通用类
class LexicalCast{
public:
    //operator() 函数的声明和定义。它重载了函数调用操作符，使得对象可以像函数一样被调用
    T operator()(const F& v){
        return boost::lexical_cast<T>(v);
    }
};
//偏特化类型，比如map、vector等
template<class T>
class LexicalCast<std::string, std::vector<T> >{
public:
    std::vector<T> operator()(const std::string& v) {
        YAML::Node node = YAML::Load(v);
        typename std::vector<T> vec;
        std::stringstream ss;
        for(size_t i = 0; i < node.size(); ++i) {
            ss.str("");
            ss << node[i];
            vec.push_back(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

template<class T>
class LexicalCast<std::vector<T>, std::string> {
public:
    std::string operator()(const std::vector<T>& v) {
        YAML::Node node;
        for(auto& i : v) {
            node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

template<class T>
class LexicalCast<std::string, std::list<T> > {
public:
    std::list<T> operator()(const std::string& v) {
        YAML::Node node = YAML::Load(v);
        typename std::list<T> vec;
        std::stringstream ss;
        for(size_t i = 0; i < node.size(); ++i) {
            ss.str("");
            ss << node[i];
            vec.push_back(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

template<class T>
class LexicalCast<std::list<T>, std::string> {
public:
    std::string operator()(const std::list<T>& v) {
        YAML::Node node;
        for(auto& i : v) {
            node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

template<class T>
class LexicalCast<std::string, std::set<T> > {
public:
    std::set<T> operator()(const std::string& v) {
        YAML::Node node = YAML::Load(v);
        typename std::set<T> vec;
        std::stringstream ss;
        for(size_t i = 0; i < node.size(); ++i) {
            ss.str("");
            ss << node[i];
            vec.insert(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

template<class T>
class LexicalCast<std::set<T>, std::string> {
public:
    std::string operator()(const std::set<T>& v) {
        YAML::Node node;
        for(auto& i : v) {
            node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

template<class T>
class LexicalCast<std::string, std::unordered_set<T> > {
public:
    std::unordered_set<T> operator()(const std::string& v) {
        YAML::Node node = YAML::Load(v);
        typename std::unordered_set<T> vec;
        std::stringstream ss;
        for(size_t i = 0; i < node.size(); ++i) {
            ss.str("");
            ss << node[i];
            vec.insert(LexicalCast<std::string, T>()(ss.str()));
        }
        return vec;
    }
};

template<class T>
class LexicalCast<std::unordered_set<T>, std::string> {
public:
    std::string operator()(const std::unordered_set<T>& v) {
        YAML::Node node;
        for(auto& i : v) {
            node.push_back(YAML::Load(LexicalCast<T, std::string>()(i)));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

template<class T>
class LexicalCast<std::string, std::map<std::string, T> > {
public:
    std::map<std::string, T> operator()(const std::string& v) {
        YAML::Node node = YAML::Load(v);
        typename std::map<std::string, T> vec;
        std::stringstream ss;
        for(auto it = node.begin();
                it != node.end(); ++it) {
            ss.str("");
            ss << it->second;
            vec.insert(std::make_pair(it->first.Scalar(),
                        LexicalCast<std::string, T>()(ss.str())));
        }
        return vec;
    }
};

template<class T>
class LexicalCast<std::map<std::string, T>, std::string> {
public:
    std::string operator()(const std::map<std::string, T>& v) {
        YAML::Node node;
        for(auto& i : v) {
            node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

template<class T>
class LexicalCast<std::string, std::unordered_map<std::string, T> > {
public:
    std::unordered_map<std::string, T> operator()(const std::string& v) {
        YAML::Node node = YAML::Load(v);
        typename std::unordered_map<std::string, T> vec;
        std::stringstream ss;
        for(auto it = node.begin();
                it != node.end(); ++it) {
            ss.str("");
            ss << it->second;
            vec.insert(std::make_pair(it->first.Scalar(),
                        LexicalCast<std::string, T>()(ss.str())));
        }
        return vec;
    }
};

template<class T>
class LexicalCast<std::unordered_map<std::string, T>, std::string> {
public:
    std::string operator()(const std::unordered_map<std::string, T>& v) {
        YAML::Node node;
        for(auto& i : v) {
            node[i.first] = YAML::Load(LexicalCast<T, std::string>()(i.second));
        }
        std::stringstream ss;
        ss << node;
        return ss.str();
    }
};

//自定义类型的模板类
class Person{
public:
    std::string m_name = "";
    int m_age = 0;
    bool m_sex = 0;

    std::string toString() const{
        std::stringstream ss;
        ss << "[Person name=" << m_name
           << "age=" << m_age
           << "sex=" << m_sex << "]";
        return ss.str();
    }

    std::ostream& operator << (std::ostream& os) {
        // 将 Person 对象的数据写入流中
        //os << person.name << " " << person.age;
        os << m_name << " " << m_age << " "<< m_sex;
        return os;
    }

    std::istream& operator>>(std::istream& is) {
        // 从流中读取数据到 Person 对象的逻辑
        // 例如，读取 person.name 和 person.age
        return is;
    }

    bool operator==(const Person& other) const {
        return m_age == other.m_age 
                && m_name==other.m_name
                && m_sex == other.m_sex;
    }

    
};

/**
  *数据类型转换-person偏特化版本
  * string -> person
  */
 template <>
 class LexicalCast<std::string, Person>{
 public:
     Person operator()(const std::string &v){
         // loads the input string as a single YAML document
         YAML::Node node = YAML::Load(v);
         Person p;
         p.m_name = node["name"].as<std::string>();
         p.m_age = node["age"].as<int>();
         p.m_sex = node["sex"].as<bool>();
         return p;
     }
 };

 /**
  * 数据类型转换-Person偏特化版本
  * Person -> string
  */
 template <>
 class LexicalCast<Person, std::string>{
 public:
     std::string operator()(const Person &v){
         YAML::Node node;
         node["name"] = v.m_name;
         node["age"] = v.m_age;
         node["sex"] = v.m_sex;
         std::stringstream ss;
         ss << node;
         return ss.str();
     }
 };

//该类使用来转换类型
template<class T, class FromStr = LexicalCast<std::string, T>, class ToStr = LexicalCast<T, std::string>>
class ConfigVar : public ConfigVarBase{
public:
    typedef std::shared_ptr<ConfigVar> ptr;
    ConfigVar(const std::string& name, const T& default_value, const std::string& description="")
    :ConfigVarBase(name, description), m_val(default_value){}

    std::string toString() override {
        try{
            //return boost::lexical_cast<std::string>(m_val);
            return ToStr()(m_val); //调用ToStr的函数，把m_val作为参数传递
        }catch(std::exception& e){
            std::cout << "ConfigVar::toString exception"
                << e.what() << " convert: " << typeid(m_val).name() << " to string";
        }
        return "";
    }

    bool fromString(const std::string& val) override{
        try{
            //m_val = boost::lexical_cast<T>(val);
            //setValue(FromStr()(val));
            setValue(FromStr()(val));
        }catch(std::exception& e){
            std::cout<< "ConfigVar::fromString exception"
                <<e.what()<<" convert: "<<typeid(m_val).name() << " to string";
        }
        return false;
    }

    const T& getValue() { return m_val;};
    void setValue(const T& val) { 
        if(m_val == val){//需要重载==运算符
            return ;
        }
        //挨个执行回调函数，类似观察者模式  为什么要挨个执行
        for(auto &i : m_cbs){
            i.second(m_val, val);
        }
        m_val = val;
    }

    std::string getTypeName() const override{return typeid(m_val).name();}

    typedef std::function<void(const T& old_value, const T& new_value)> on_change_cb;//改变配置文件时，调用该回调函数进行通知
    std::map<uint64_t, on_change_cb> m_cbs;    //存放回调函数的map容器

    //增加监听的回调函数
    void addListmap(uint64_t key, on_change_cb cb){
        m_cbs[key] = cb;
    }
    //删除监听的回调函数
    void delListmap(uint64_t key){
        m_cbs.erase(key);
    }
    //获取想要的监听器
    on_change_cb getListmap(uint64_t key){
        auto it = m_cbs.find(key);
        return it == m_cbs.end() ? nullptr : it;
    }
    //清空监听器
    void clearListmap(){
        m_cbs.clear();
    }

private:
    T m_val;
};

//该类解释yaml文件
class Config {
public:
    typedef std::unordered_map<std::string, ConfigVarBase::ptr> ConfigVarMap;

    template<class T>//创建和查找name
    static typename ConfigVar<T>::ptr Lookup(const std::string& name,
            const T& default_value, const std::string& description = "") {
        auto it = GetDatas().find(name);
        if(it != GetDatas().end()) {
        	//智能指针的强转 可以查看我的 【配置系统-知识储备篇】
            auto tmp = std::dynamic_pointer_cast<ConfigVar<T> >(it->second);
            if(tmp) { //在map中存在name
                //SYLAR_LOG_INFO(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists";
                std::cout<<"Lookup name = "<<name<<std::endl;
                return tmp; //存在就返回
            } else {    //存在但是类型不同或者不存在
                // SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name=" << name << " exists but type not "
                //         << typeid(T).name() << " real_type=" << it->second->getTypeName()
                //         << " " << it->second->toString();
                std::cout<<"Lookup name = error" << std::endl;
                return nullptr;
            }
        }
        //查找name中是否存在abcdefghijklmnopqrstuvwxyz._0123456789以外的字符
        if(name.find_first_not_of("abcdefghijklmnopqrstuvwxyz._0123456789")
                != std::string::npos) { //如果查到了
            //SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Lookup name invalid " << name;
            std::cout<<"Lookup name invalid " << name <<std::endl;
            throw std::invalid_argument(name); //抛出异常
        }
        //在map中没有并且name符合条件，添加进map中
        typename ConfigVar<T>::ptr v(new ConfigVar<T>(name, default_value, description));
        GetDatas()[name] = v;   //这是一个赋值语句，在map中增加一个元素为<name,v>
        return v;
    }

    template<class T>
    static typename ConfigVar<T>::ptr Lookup(const std::string& name) {//在map表中是否存在name
        auto it = GetDatas().find(name);
        if(it == GetDatas().end()) {
            return nullptr;
        }
        return std::dynamic_pointer_cast<ConfigVar<T> >(it->second);
    }

    static void LoadFromYaml(const YAML::Node& root);   //覆盖原有配置的核心方法
    static ConfigVarBase::ptr LookupBase(const std::string& name);  //查找map中的配置name
private:
    //设置了一个私有的单例
    static ConfigVarMap& GetDatas() {
        static ConfigVarMap s_datas;
        return s_datas;
    }
};

//解释yaml文件的核心函数
static void ListAllMember(const std::string& prefix,
                          const YAML::Node& node,
                          std::list<std::pair<std::string, const YAML::Node> >& output) {
    if(prefix.find_first_not_of("abcdefghikjlmnopqrstuvwxyz._0123456789")
            != std::string::npos) {
        //SYLAR_LOG_ERROR(SYLAR_LOG_ROOT()) << "Config invalid name: " << prefix << " : " << node;
        std::cout<<"Config invalid name: "<<prefix<<std::endl;
        return;
    }
    //把所有的内容添加到node里
    output.push_back(std::make_pair(prefix, node));
    if(node.IsMap()) {  //判断yaml节点是否是一个映射节点
        for(auto it = node.begin();
                it != node.end(); ++it) {
            ListAllMember(prefix.empty() ? it->first.Scalar() //递归解析
                    : prefix + "." + it->first.Scalar(), it->second, output);
        }
    }
}

void Config::LoadFromYaml(const YAML::Node &root){
    // 结点类型<string,YAML::Node>
    std::list<std::pair<std::string, const YAML::Node>> all_nodes;
    // 将root中的结点进行解析，存放到all_nodes中
    ListAllMember("", root, all_nodes);

    for (auto &i : all_nodes){
        // 遍历，获取key，查找是否包含key，如果包含，将之前修改为从文件中加载的值
        std::string key = i.first;
        if (key.empty()){
            continue;
        }
        // 将key转为小写
        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        // 查询是否包含key
        ConfigVarBase::ptr var = LookupBase(key);

        // 如果存在key才从文件中加载更新，不存在直接跳过
        if (var){
            if (i.second.IsScalar()){
                // 将YAML::内结点值转为Scalar类型
                // 然后从字符串中加载（已通过实现偏特化实现了类型的转换），设置m_val，进行更新
                var->fromString(i.second.Scalar());
            }
            else{
                // 其他类型 Sequence,偏特化中fromString有对应的处理方法
                std::stringstream ss;
                ss << i.second;
                var->fromString(ss.str());
            }
        }
    }
}

ConfigVarBase::ptr Config::LookupBase(const std::string& name) {
    auto it = GetDatas().find(name);
    return it == GetDatas().end() ? nullptr : it->second;
}


int main(){
    //YAML::Node root = YAML::LoadFile("test.yaml");   //加载出来最原本的文件内容
    // //std::cout<<root<<std::endl;
    // std::list<std::pair<std::string, const YAML::Node> > all_nodes;
    // // 将root中的结点进行解析，存放到all_nodes中
    // ListAllMember("", root, all_nodes);
    // // 遍历输出all_nodes
    // for (auto it = all_nodes.begin(); it != all_nodes.end(); it++){
    //     std::cout << it->first << std::endl;
    // }

    ConfigVar<Person>::ptr g_person = Config::Lookup("class.person", Person(), "class person");
    // 使用lambda表达式定义回调函数
    g_person->addListmap(10, [](const Person &old_value, const Person &new_value)
    { std::cout << "listener function "
    << "old value= " << old_value.toString()
    << " new value " << new_value.toString() << std::endl; });
    std::cout << "before" << g_person->getValue().toString() << "\n"
    << g_person->toString() << std::endl;
    YAML::Node root = YAML::LoadFile("test.yaml");
    Config::LoadFromYaml(root);
    std::cout << "after" << g_person->getValue().toString() << "\n"
    << g_person->toString() << std::endl;
    //使用复杂类型
    ConfigVar<std::map<std::string, Person>>::ptr g_map_person = 
        Config::Lookup("class.map", std::map<std::string, Person>(), "class map person");
    Config::LoadFromYaml(root);
    auto it = g_map_person->getValue();
    for(auto& i : it){
        std::cout<< i.first << " - " << i.second.toString() <<std::endl;
    }
    

#define XX(g_var, name, prefix) \
    { \
        auto& v = g_var->getValue(); \
        for(auto& i : v) { \
            std::cout << #prefix " " #name ": " << i << std::endl; \
        } \
        std::cout << #prefix " " #name " yaml: " << g_var->toString() << std::endl; \
    }

#define XX_M(g_var, name, prefix) \
    { \
        auto& v = g_var->getValue(); \
        for(auto& i : v) { \
            std::cout << #prefix " " #name ": {" << i.first<< "-" << i.second << "}" << std::endl; \
        } \
        std::cout << #prefix " " #name " yaml: " << g_var->toString() << std::endl; \
    }

    ConfigVar<int>::ptr g_int_value_config = Config::Lookup("system.port", (int)8080, "system port");
    ConfigVar<std::vector<int>>::ptr g_vector_int_config = 
            Config::Lookup("system.int_vec", std::vector<int>{1,2}, "system int vec");
    ConfigVar<std::set<int>>::ptr g_set_int_config = 
            Config::Lookup("system.int_set", std::set<int>{1,2}, "system int set");
    ConfigVar<std::map<std::string, int>>::ptr g_map_str_int_config = 
            Config::Lookup("system.str_int_map", std::map<std::string, int>{{"k",2}}, "system str int map");

    std::cout<<"before " << g_int_value_config->getValue()<<std::endl;
    XX(g_vector_int_config, system.int_vec, before);
    XX(g_set_int_config, system.int_set, before);
    XX_M(g_map_str_int_config, system.str_int_map, before);
    

    //YAML::Node root = YAML::LoadFile("test.yaml");
    Config::LoadFromYaml(root);

    std::cout<<"after " << g_int_value_config->getValue()<<std::endl;
    XX(g_vector_int_config, system.int_vec, after);
    XX(g_set_int_config, system.int_set, after);
    XX_M(g_map_str_int_config, system.str_int_map, after);


    return 0;
}


